/**
 * 
 */
package auction;

import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;

import security.HMACutil;
import security.ServerAuthenticationChannel;
import security.TCPChannel;
import security.TimestampUtil;

/**
 * Thread der die Verbindung zu einem Client abwickelt.
 * 
 * @author Gernot
 * 
 */
public class ClientHandler extends Thread {
    private static final String LOGIN_REGEX = "!login [A-Za-z0-9]+ \\d+ .*"; // login
                                                                          // usrname
                                                                          // clientudpport

    //private Socket s;
    private ServerAuthenticationChannel channel;
    //private InputStreamReader isr;
    //private BufferedReader br;
    private AccountManager am;
    // private DatagramSocket udp_s;
    // private int port;
    private String username = "";
    private String clientTimestampPort = "";
    private String clientIP;
    boolean running;
    //private OutputStreamWriter writer;

    // private OutputStreamWriter out;

    public ClientHandler(Socket socket, AccountManager am) throws IOException {
        channel = new ServerAuthenticationChannel(new TCPChannel(socket));
        clientIP = socket.getInetAddress().getHostAddress();
        // s.setSoTimeout(60000);
        this.am = am;
        this.running = true;
        // out = new OutputStreamWriter(s.getOutputStream());
        AccountManager.instance.addConnection(this);
    }

    /**
     * Stop Thread by closing socket Free all ressources
     * 
     * @throws IOException
     */
    public void stopClientHandler() {
        this.interrupt();

        try {
            channel.close();
        } catch (IOException e) {
            // e.printStackTrace();
        }
        // udp_s.close();
    }

    @Override
    public void run() {
        // System.out.println("Starting ClientHandler");
        String str;
        // boolean loggedIn = false;
        try {
            while (running && ((str = channel.receive()) != null)) {

                // System.out.println("RECEIVED:"+str);

                if (str.matches("!login.*")) {

                    if (!am.isLoggedIn(this)) {
                        String[] cmd = ClientHandler.checkLogin(str);
                        if (cmd != null) {
                            username = cmd[1];
                            clientTimestampPort = cmd[2];
                            // port = Integer.parseInt(cmd[2]);

                            // udp_s = new DatagramSocket();

                            if (am.login(username, this)) {
                            	try {
                            		channel.loginProcess(str);
                                    // loggedIn = true;
                                    this.sendTCPMessage("Successfully logged in as "
                                            + username + "!");
                            	} catch (SecurityException e) {
                            		am.logout(username);
                            	}
                            } else {
                            	channel.stopSecurity();
                                this.sendTCPMessage("Login failed! User already logged in!");
                            }
                        } else {
                        	channel.stopSecurity();
                            this.sendTCPMessage("Login failed! Invalid login command!");
                            // incorrect login cmd
                        }
                    } else {
                        this.sendTCPMessage("You are already logged in!");
                    }
                } else if (str.matches("!logout")) {
                    if (am.isLoginValid(username, this)) {
                        if (am.logout(username)) {
                            // loggedIn = false;
                            channel.stopSecurity("Successfully logged out as "
                                    + username + "!");
                        } else {
                            this.sendTCPMessage("Logout failed!");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }
                } else if (str.matches("!list")) {
                    if (am.isLoginValid(username, this)){
                        ArrayList<String> list = am.listAuctions();
    
                        String wholeMessage = "";
                        for (String auction : list) {
                            this.sendTCPMessage(auction);
                            wholeMessage += auction + '\n';
                        }
                        this.sendTCPMessage("!endlist " + HMACutil.getHMAC(wholeMessage + "!endlist", username));
                    } else {
                        ArrayList<String> list = am.listAuctions();
    
                        for (String auction : list) {
                            this.sendTCPMessage(auction);
                        }
                        this.sendTCPMessage("!endlist");
                    }
                    // !create <at least one digit> <at least one
                    // Non-whitspace-character>
                } else if (str.matches("!create \\d+ \\S+.*")) {
                    if (am.isLoginValid(username, this)) {
                        String[] cmd = str.split(" ");
                        if (cmd.length > 2) {

                            // Connect Name to 1String.
                            String name = cmd[2];
                            for (int i = 3; i < cmd.length; i++) {
                                name = name + " " + cmd[i];
                            }

                            int duration = Integer.parseInt(cmd[1]);

                            String response = am.createAuction(name,
                                    this.username, duration);
                            this.sendTCPMessage(response);

                        } else {// else regex should fail
                            System.out
                                    .println("Should never happen, regex fail");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }
                } else if (str.matches("!bid \\d+ \\d+\\.?\\d*")) {
                    if (am.isLoginValid(username, this)) {
                        String[] cmd = str.split(" ");
                        if (cmd.length == 3) {

                            int id = Integer.parseInt(cmd[1]);
                            double amount = Double.parseDouble(cmd[2]);

                            String response = am.bidAuction(username, id,
                                    amount);
                            this.sendTCPMessage(response);

                        } else {// else regex should fail
                            System.out
                                    .println("Should never happen, regex fail");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }

                } else if (str.matches("!groupBid \\d+ \\d+\\.?\\d*")) {
                    if (am.isLoginValid(username, this)) {
                        String[] cmd = str.split(" ");
                        if (cmd.length == 3) {

                            int id = Integer.parseInt(cmd[1]);
                            double amount = Double.parseDouble(cmd[2]);

                            String response = am.initGroupBid(username, id, amount);
                            System.out.println(response);
                            this.sendTCPMessage(response);

                        } else {// else regex should fail
                            System.out
                                    .println("Should never happen, regex fail");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }

                } else if (str.matches("!confirm \\d+ \\d+\\.?\\d* \\S+")) {
                    if (am.isLoginValid(username, this)) {
                        String[] cmd = str.split(" ");
                        if (cmd.length == 4) {

                            int id = Integer.parseInt(cmd[1]);
                            double amount = Double.parseDouble(cmd[2]);
                            String create_user = cmd[3];
                            
                            String response = am.confirmGroupBid(username, id, amount, create_user);
                            if (response != null) {
                                this.sendTCPMessage("!rejected "+response);
                            }
                            
                        } else {// else regex should fail
                            System.out
                                    .println("Should never happen, regex fail");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }

                    
                } else if (str.equals("!getClientList")) {
                    if (am.isLoginValid(username, this)){
                    	String[] list = am.getClientList();
                        String wholeMessage = "Active Clients:\n";
                    	sendTCPMessage("Active Clients:");
                    	for (String entry : list) {
                    		sendTCPMessage(entry);
                    		wholeMessage += entry + '\n';
                    	}
                    	sendTCPMessage("!endlist " + HMACutil.getHMAC(wholeMessage + "!endlist", username));
                    } else {
                    	sendTCPMessage("You are not logged in!");
                    }
                    
                    
                } else if (str.matches("!signedBid\\s+\\d+\\s+\\d+\\.?\\d*\\s+\\S+?:\\d+:\\S+?\\s+\\S+?:\\d+:\\S+?")) {
                    if (am.isLoginValid(username, this)) {
                    	String[] cmd = str.split("\\s+");
                        if (cmd.length == 5) {
                            int id = Integer.parseInt(cmd[1]);
                            double amount = Double.parseDouble(cmd[2]);
                            String[] signs = {cmd[3], cmd[4]};
                            long timestamp = TimestampUtil.getTimestamp(cmd[1], cmd[2], signs);
                            if (timestamp != 0l) {
	                            String response = am.signedBidAuction(username, id, amount, timestamp);
	                            this.sendTCPMessage(response);
                            } else {
                            	System.out.println("corrupt signed bid denied");
                            }

                        } else {// else regex should fail
                            System.out.println("Should never happen, regex fail");
                        }
                    } else {
                        this.sendTCPMessage("You are not logged in!");
                    }
                } else {
                    this.sendTCPMessage("Unrecognized Command: '" + str + "'");
                }

            }

        } catch (IOException e) {
            System.out.println("Error: " + e.getMessage());
            if (!username.equals("")) {
                AccountManager.instance.reportUserEvent("USER_DISCONNECTED",
                        username);
            }
        }
        if (!this.username.equals("")) {
            am.logout_timeout(this.username);
        }
        am.removeConnection(this);
        running = false;
        this.stopClientHandler();
        System.out.println("ClientHandlerThread ended");
    }

    public void sendUDPMessage(String msg) throws IOException {
        // System.out.println("Sending UDP Message:"+msg);

        // JUST DO NOTHING FOR LAB2
        /*
         * byte[] data = msg.getBytes(); DatagramPacket packet = new
         * DatagramPacket(data, data.length, s.getInetAddress(), port);
         * udp_s.send(packet);
         */
    }

    public void sendTCPMessage(String msg) throws IOException {
        channel.send(msg);
    }

    public static String[] checkLogin(String str) {

        if (str.matches(LOGIN_REGEX)) {
            String[] cmd;
            cmd = str.split(" ");
            return cmd;
        }

        return null;
    }

	/**
	 * @return the clientTimestampPort
	 */
	public String getClientTimestampPort() {
		return clientTimestampPort;
	}

	/**
	 * @return the clientIP
	 */
	public String getClientIP() {
		return clientIP;
	}
}
